package org.ly.uap.client.validation;

import org.ly.uap.client.util.AbstractCasFilter;
import org.ly.uap.client.util.CommonUtils;
import org.ly.uap.client.util.ReflectUtils;

import javax.net.ssl.HostnameVerifier;
import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

public abstract class AbstractTicketValidationFilter extends AbstractCasFilter {
    private TicketValidator ticketValidator;
    private boolean redirectAfterValidation = false;

    private boolean exceptionOnValidationFailure = true;

    private boolean useSession = true;

    protected TicketValidator getTicketValidator(FilterConfig filterConfig) {
        return this.ticketValidator;
    }

    protected HostnameVerifier getHostnameVerifier(FilterConfig filterConfig) {
        String className = getPropertyFromInitParams(filterConfig, "hostnameVerifier", null);
        this.log.trace("Using hostnameVerifier parameter: " + className);
        String config = getPropertyFromInitParams(filterConfig, "hostnameVerifierConfig", null);
        this.log.trace("Using hostnameVerifierConfig parameter: " + config);
        if (className != null) {
            return (HostnameVerifier) ReflectUtils.newInstance(className, new Object[]{config});
        }
        return null;
    }

    protected void initInternal(FilterConfig filterConfig) throws ServletException {
        setExceptionOnValidationFailure(parseBoolean(getPropertyFromInitParams(filterConfig, "exceptionOnValidationFailure", "true")));
        this.log.trace("Setting exceptionOnValidationFailure parameter: " + this.exceptionOnValidationFailure);
        setRedirectAfterValidation(parseBoolean(getPropertyFromInitParams(filterConfig, "redirectAfterValidation", "true")));
        this.log.trace("Setting redirectAfterValidation parameter: " + this.redirectAfterValidation);
        setUseSession(parseBoolean(getPropertyFromInitParams(filterConfig, "useSession", "true")));
        this.log.trace("Setting useSession parameter: " + this.useSession);
        setTicketValidator(getTicketValidator(filterConfig));
        super.initInternal(filterConfig);
    }

    public void init() {
        super.init();
        CommonUtils.assertNotNull(this.ticketValidator, "ticketValidator cannot be null.");
    }

    protected boolean preFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain)
            throws IOException, ServletException {
        return true;
    }

    protected void onSuccessfulValidation(HttpServletRequest request, HttpServletResponse response, Assertion assertion) {
    }

    protected void onFailedValidation(HttpServletRequest request, HttpServletResponse response) {
    }

    public final void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain)
            throws IOException, ServletException {
        if (!preFilter(servletRequest, servletResponse, filterChain)) {
            return;
        }

        HttpServletRequest request = (HttpServletRequest) servletRequest;
        HttpServletResponse response = (HttpServletResponse) servletResponse;
        String ticket = CommonUtils.safeGetParameter(request, getArtifactParameterName());

        if (CommonUtils.isNotBlank(ticket)) {
            if (this.log.isDebugEnabled()) {
                this.log.debug("Attempting to validate ticket: " + ticket);
            }
            try {
                Assertion assertion = this.ticketValidator.validate(ticket, constructServiceUrl(request, response));

                if (this.log.isDebugEnabled()) {
                    this.log.debug("Successfully authenticated user: " + assertion.getPrincipal().getName());
                }

                request.setAttribute("_const_cas_assertion_", assertion);

                if (this.useSession) {
                    request.getSession().setAttribute("_const_cas_assertion_", assertion);
                }
                onSuccessfulValidation(request, response, assertion);

                if (this.redirectAfterValidation) {
                    this.log.debug("Redirecting after successful ticket validation.");
                    response.sendRedirect(constructServiceUrl(request, response));
                    return;
                }
            } catch (TicketValidationException e) {
                response.setStatus(403);
                this.log.warn(e, e);

                onFailedValidation(request, response);

                if (this.exceptionOnValidationFailure) {
                    throw new ServletException(e);
                }

                return;
            }
        }

        filterChain.doFilter(request, response);
    }

    public final void setTicketValidator(TicketValidator ticketValidator) {
        this.ticketValidator = ticketValidator;
    }

    public final void setRedirectAfterValidation(boolean redirectAfterValidation) {
        this.redirectAfterValidation = redirectAfterValidation;
    }

    public final void setExceptionOnValidationFailure(boolean exceptionOnValidationFailure) {
        this.exceptionOnValidationFailure = exceptionOnValidationFailure;
    }

    public final void setUseSession(boolean useSession) {
        this.useSession = useSession;
    }
}

